#pragma once

// 1.setsid()
// 2.调用setsid()的进程不能是进程组的组长。这个很简单，因为你要单干，你得先卸任你的组长，或者你根本就不是组长~
//   如果组长调了这个函数，会出错，Operation not permitted
// 3.守护进程
//   a. 忽略异常信号
//   b. 0，1，2号fd特殊处理
//   c. 进程的工作目录(ls /proc/pid)可能要更改

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>
#include "log.hpp"
#include "err.hpp"

void Daemon()
{
    // 忽略异常信号
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    if (fork() > 0) // 父进程直接退了，接下来交给子进程，因为父进程是第一个进程，即进程组的组长
        exit(0);
    // 所以守护进程，本质上是孤儿进程。

    // 新建会话，自己成为会话的话首进程，即会话里只有我这个任务。
    pid_t ret = setsid();
    if ((int)ret == -1)
    {
        logMessage(FATAL, "Daemon error. %s", strerror(errno));
        exit(SETSID_ERR);
    }

    // 可选：更改进程工作目录，工作目录决定了文件以相对路径打开时以什么路径作为参考
    // chdir("/");

    // 处理0、1、2文件描述符的问题，因为守护进程和终端没关系了，不能再从键盘输入、不能再打印到显示器。
    // 因为咱们的程序里可能存在cin、cout、cerr，所以也不能简单粗暴的close掉0、1、2
    // ls /dev/null   任何一个Linux系统里都有这个字符文件，往这个文件里写东西读东西没有任何反应。
    int fd = open("/dev/null", O_RDWR);
    if(fd < 0)
    {
        logMessage(FATAL, "open error. %s", strerror(errno));
        exit(OPEN_ERR);
    }
    // 将0、1、2fd重定向到fd中
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
}